import type { CrudFilter, CrudFilters, CrudOperators, CrudSorting, } from "@refinedev/core";
import setWith from "lodash/setWith";
import set from "lodash/set";
import mapKeys from "lodash/mapKeys";
import camelCase from "camelcase";
import { mapValues } from "lodash";

export const upperCaseValues = (obj: any): any => {
    if (!obj) return undefined;
    return mapValues(obj, (v: string) => v.toUpperCase());
};

export const camelizeKeys = (obj: any): any => {
    if (!obj) return undefined;
    return mapKeys(obj, (_v, k) => camelCase(k));
};

export type HasuraFilterCondition =
    | "_and"
    | "_not"
    | "_or"
    | "_eq"
    | "_gt"
    | "_gte"
    | "_lt"
    | "_lte"
    | "_neq"
    | "_in"
    | "_nin"
    | "_like"
    | "_nlike"
    | "_ilike"
    | "_nilike"
    | "_is_null"
    | "_similar"
    | "_nsimilar"
    | "_regex"
    | "_iregex"
    | "_nest";

const hasuraFilters: Record<string, HasuraFilterCondition | undefined> =
{
    eq: "_eq",
    ne: "_neq",
    lt: "_lt",
    gt: "_gt",
    lte: "_lte",
    gte: "_gte",
    in: "_in",
    nin: "_nin",
    contains: "_ilike",
    ncontains: "_nilike",
    containss: "_like",
    ncontainss: "_nlike",
    null: "_is_null",
    or: "_or",
    and: "_and",
    between: undefined,
    nbetween: undefined,
    nnull: "_is_null",
    startswith: "_iregex",
    nstartswith: "_iregex",
    endswith: "_iregex",
    nendswith: "_iregex",
    startswiths: "_similar",
    nstartswiths: "_nsimilar",
    endswiths: "_similar",
    nendswiths: "_nsimilar",
    nest: "_nest"
};

export const handleFilterValue = (operator: CrudOperators, value: any) => {
    switch (operator) {
        case "startswiths":
        case "nstartswiths":
            return `${value}%`;
        case "endswiths":
        case "nendswiths":
            return `%${value}`;
        case "startswith":
            return `^${value}`;
        case "nstartswith":
            return `^(?!${value})`;
        case "endswith":
            return `${value}$`;
        case "nendswith":
            return `(?<!${value})$`;
        case "nnull":
            return false;
        case "contains":
        case "containss":
        case "ncontains":
        case "ncontainss":
            return `%${value}%`;
        default:
            return value;
    }
};
export const generateNestedFilterQuery = (filter: CrudFilter): any => {
    const { operator } = filter;

    if (operator !== "or" && operator !== "and" && "field" in filter) {
        const { field, value } = filter;

        const hasuraOperator = hasuraFilters[filter.operator];
        if (!hasuraOperator) {
            throw new Error(`Operator ${operator} is not supported`);
        }
        const fieldsArray = field.split(".");
        const fieldsWithOperator = [...fieldsArray, hasuraOperator];
        const filteredValue = handleFilterValue(operator, value);
        return {
            ...setWith({}, fieldsWithOperator, filteredValue, Object),
        };
    }
    const key = filter.key;
    if(key){
        return {
            [`${key}`]: {
                [`_${operator}`]: filter.value.map((f) => generateNestedFilterQuery(f)),
            }
        }
    }
    return {
        [`_${operator}`]: filter.value.map((f) => generateNestedFilterQuery(f)),
    };
};
export const generateFilters: any = (filters?: CrudFilters) => {
    if (!filters) {
        return undefined;
    }
    const nestedQuery = generateNestedFilterQuery({
        operator: "and",
        value: filters,
    });
    return nestedQuery;
};
export type HasuraSortingType = Record<string, "asc" | "desc">;

export type GenerateSortingType = {
    (sorting?: CrudSorting): HasuraSortingType | undefined;
};
export const generateSorting: GenerateSortingType = (sorters?: CrudSorting) => {
    if (!sorters) {
        return undefined;
    }

    const sortingQueryResult: Record<
        string,
        "asc" | "desc" | HasuraSortingType
    > = {};

    sorters.forEach((sortItem) => {
        set(sortingQueryResult, sortItem.field, sortItem.order);
    });

    return sortingQueryResult as HasuraSortingType;
};
